package vn.mahjonggame.game.request;

import java.util.Collections;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.log4j.Logger;

import vn.mahjonggame.game.Mahjong;
import vn.mahjonggame.game.MahjongExtension;
import vn.mahjonggame.metadata.GameActionType;
import vn.mahjonggame.metadata.MatchState;
import vn.mahjonggame.metadata.Player;
import vn.mahjonggame.metadata.PlayerMode;
import vn.mahjonggame.metadata.MObject;
import vn.mahjonggame.metadata.Response;
import vn.mahjonggame.metadata.RoomVarName;
import vn.mahjonggame.metadata.Tile;
import vn.mahjonggame.metadata.TileSet;
import vn.mahjonggame.metadata.TileType;
import vn.mahjonggame.metadata.UserVarName;
import vn.mahjonggame.platform.GameplayConfig;
import vn.mahjonggame.platform.PlatformClient;
import vn.mahjonggame.platform.PlatformConfig;
import vn.mahjonggame.platform.ServerMode;
import vn.mahjonggame.util.PlayerComparator;

import com.smartfoxserver.v2.entities.Room;
import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.entities.data.ISFSObject;
import com.smartfoxserver.v2.entities.data.SFSArray;
import com.smartfoxserver.v2.entities.data.SFSObject;
import com.smartfoxserver.v2.extensions.BaseClientRequestHandler;
import com.smartfoxserver.v2.extensions.ExtensionLogLevel;

public class PickTileHandler extends BaseClientRequestHandler {

	@Override
	public void handleClientRequest(User user, ISFSObject params) {
		
		MahjongExtension ext = (MahjongExtension) getParentExtension();
				
		pickTile(ext, user, params, false);
	}
	
	public void pickTile(final MahjongExtension ext, final User user, ISFSObject params, boolean isFromDeathWall){
		setParentExtension(ext);
		
		Mahjong mahjong = ext.getMahjong();		
		final Player player = mahjong.getPlayerById(user.getId());
		Player playerInTurn = mahjong.getPlayerInTurn();
		Player pointingPlayer = mahjong.getPointingPlayer();
		List<Player> players = mahjong.getPlayers();
		Logger logger = mahjong.getLogger();		
		Room room = ext.getGameRoom();
		String serverMode = room.getVariable(PlatformConfig.SERVER_MODE).getStringValue();
		List<User> users = room.getUserList();
		
		if(!player.equals(playerInTurn)){		
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "playerInTurn is " + playerInTurn.toString()));
			return;
		}
		
		if(player.hasPicked()){
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "has picked"));
			return;
		}
		
		/*
		 * For pick from death wall
		 */
		String kanType = null;
		/*
		 * Pick a new DORA
		 */
		Tile newDora = null;
		
		
		List<TileSet> skipTypes =  player.getSkipTypes();
		skipTypes.clear();
		
		if(player == null){
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "player " + player.toString() + " did not join game."));
			return;
		}
		
		mahjong.setCallingPickPlayer(null);
		
		if(mahjong.getCanCallRonPlayers().size() > 0 && !mahjong.getCanCallRonPlayers().contains(player)){			
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Mahjong has player who can call RON"));
			mahjong.setCallingPickPlayer(player);
			return;
		}
		
		if(mahjong.getCanCallOpenKanPlayer() != null && !mahjong.getCanCallOpenKanPlayer().equals(player)){
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Mahjong has player who can call OPENKAN"));
			mahjong.setCallingPickPlayer(player);
			return;
		}
		
		if(mahjong.getCanCallPonPlayer() != null && !mahjong.getCanCallPonPlayer().equals(player)){
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Mahjong has player who can call PON"));
			mahjong.setCallingPickPlayer(player);
			return;
		}
		
		if(mahjong.getCanCallChiPlayer() != null && !mahjong.getCanCallChiPlayer().equals(player)){
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Mahjong has player who can call CHI"));
			mahjong.setCallingPickPlayer(player);
			return;
		}
		
		Player nextPlayer = mahjong.getNextPlayer(player);
		
		if(isFromDeathWall){
			kanType = params.getUtfString(MObject.kanType.toString());
		}
				
		/*
		if(mahjong.getNumTileOnWall() == 0){
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "NumTileOnWall is 0"));
			return;
		}*/
		
		/*
		 * Pick a tile from the wall 
		 */
		Tile tile = null;
		

		/*
		 * Pick a tile from the death wall 
		 */
		if(isFromDeathWall){
			tile = mahjong.pickTileOnDeathWall();
			mahjong.getPickedDeathTile().add(tile);
			newDora = mahjong.pickTheNextDora();
			mahjong.getDoras().add(newDora);
			
			logger.info(mahjong.log(room, player, GameActionType.pickTileFromDeathWall.toString(), "kanType: " + kanType));
			logger.info(mahjong.log(room, player, GameActionType.pickTileFromDeathWall.toString(), "tileOnDeathWall: " + tile));
			logger.info(mahjong.log(room, player, GameActionType.pickTileFromDeathWall.toString(), "theNextDora: " + newDora));
			logger.info(mahjong.log(room, player, GameActionType.pickTileFromDeathWall.toString(), "getPickedDeathTile: " + mahjong.getPickedDeathTile()));
			logger.info(mahjong.log(room, player, GameActionType.pickTileFromDeathWall.toString(), "getDoras: " + mahjong.getDoras()));
		}
		else {
			if(serverMode.equalsIgnoreCase(ServerMode.DEBUG.toString())){
				if(player.getWillPickTiles().size() > 0){
					tile = player.getWillPickTiles().remove(0);
				}				
			}
			
			if(tile == null){
				tile = mahjong.pickTile();
			}
		}
					
		/*
		 * Check if player can call TSUMO
		 */
		player.checkTiles(tile);
		player.chiitoisu();
		player.kokushiMusou(tile);
		
		/*
		 * Check if player can call RiiChi
		 */
		player.tenpai();
		player.riichi();
		
		if(player.isTenpai()){
			mahjong.getTenpaiPlayers().add(player);
		}
		
		Response.Builder response = new Response.Builder();		
		response.tile(tile.getId())
				.position(player.getPlayerPosition())
				.nextPosition(nextPlayer.getPlayerPosition())
				.turn(player.getPlayerPosition())
				.turnTime(player.getTurnTime()/1000)
				.bonusTime(player.getBonusTime()/1000);
		
		if(isFromDeathWall && kanType.equalsIgnoreCase(TileSet.CLOSEKAN.toString())){
			response.doras(Arrays.asList(newDora.getId()));
		}
							
		/*
		 * Check if the MAHJONG is HOA
		 * Check TENPAI player
		 */
		if(mahjong.getNumTileOnWall() == 0){
			for(Iterator<Player> iPlayers = mahjong.getPlayers().iterator(); iPlayers.hasNext();){
				Player iPlayer = iPlayers.next();
				if(iPlayer.equals(player)) continue;
				
				Tile firstTile = iPlayer.getLeftTiles().get(0);				
				iPlayer.checkTiles(firstTile);				
				iPlayer.tenpai();
				
				if(iPlayer.isTenpai()){
					mahjong.getTenpaiPlayers().add(iPlayer);
				}
			}
		}
		
		if(mahjong.getNumTileOnWall() == 0){
			int numTenpaiPlayer = mahjong.getTenpaiPlayers().size();
			if(numTenpaiPlayer == 0){
				
				Response.Builder object = new Response.Builder();
				
				SFSArray sfsPlayerArray = new SFSArray();
				
				for(int i = 0; i < players.size(); i++){
					
					Player iPlayer = players.get(i);
					/*
					 * Add tile directly to the left tiles if this is the final tile on the wall
					 */
					iPlayer.getLeftTiles().add(tile);
					
					Response.Builder sfsUser = new Response.Builder();
					sfsUser.name(iPlayer.getName())
							.point(0)
							.money(0)
							.isWinner(false)
							.tiles(mahjong.tileArrayToTileIdArray(iPlayer.getLeftTiles()));
					logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Player " + iPlayer.toString() + " " + iPlayer.getLeftTiles()));
					sfsPlayerArray.addSFSObject(sfsUser.build().getSFSObject());
				}
					
				object.winPos(player.getPlayerPosition())
						.losePos(player.getPlayerPosition())
						.type(1);
								
				response.HOA(object.build().getSFSObject())
						.players(sfsPlayerArray);
				send(TileSet.HOA.toString(), response.build().getSFSObject(), users);
				ext.setMatchState(MatchState.SHOW_RESULT);
				return;
			}
			else if (numTenpaiPlayer == 1){
				for(Iterator<Player> iPlayers = mahjong.getPlayers().iterator(); iPlayers.hasNext();){
					Player iPlayer = iPlayers.next();					
					if(iPlayer.isTenpai()){
						double addedScore = 3000;
						double finalScore = iPlayer.getFinalScore();
						finalScore = finalScore + addedScore;
						iPlayer.setFinalScore(finalScore);
						iPlayer.setExtendedScore(addedScore);
					}
					else {
						double minusScore = 1000;
						
						double finalScore = iPlayer.getFinalScore();
						finalScore = finalScore - minusScore;
						iPlayer.setFinalScore(finalScore);
						iPlayer.setExtendedScore(-minusScore);
					}
				}
			}
			else if (numTenpaiPlayer == 2){
				for(Iterator<Player> iPlayers = mahjong.getPlayers().iterator(); iPlayers.hasNext();){
					Player iPlayer = iPlayers.next();					
					if(iPlayer.isTenpai()){
						double addedScore = 1500;
						double finalScore = iPlayer.getFinalScore();
						finalScore = finalScore + addedScore;
						iPlayer.setFinalScore(finalScore);
						iPlayer.setExtendedScore(addedScore);
					}
					else {
						double minusScore = 1500;
						
						double finalScore = iPlayer.getFinalScore();
						finalScore = finalScore - minusScore;
						iPlayer.setFinalScore(finalScore);
						iPlayer.setExtendedScore(-minusScore);
					}
				}
			}
			else if (numTenpaiPlayer == 3){
				for(Iterator<Player> iPlayers = mahjong.getPlayers().iterator(); iPlayers.hasNext();){
					Player iPlayer = iPlayers.next();					
					if(iPlayer.isTenpai()){
						double addedScore = 1000;
						double finalScore = iPlayer.getFinalScore();
						finalScore = finalScore + addedScore;
						iPlayer.setFinalScore(finalScore);
						iPlayer.setExtendedScore(addedScore);
					}
					else {
						double minusScore = 3000;
						
						double finalScore = iPlayer.getFinalScore();
						finalScore = finalScore - minusScore;
						iPlayer.setFinalScore(finalScore);
						iPlayer.setExtendedScore(-minusScore);
					}
				}
			}
			
			int roomBet = room.getVariable(RoomVarName.roomBet.toString()).getIntValue();
			
			/*
			 * Call game_finish_game API
			 */
			
			/*
			SFSArray userAmounts = new SFSArray();
			
			for(int i = 0; i < users.size(); i++){
				User iUser = users.get(i);				
				Response.Builder userAmount = new Response.Builder();
				Player iPlayer = mahjong.getPlayerById(iUser.getId());
				userAmount.user_id(iUser.getName());				
								
				double amount = (iPlayer.getExtendedScore() * roomBet) / Double.valueOf(room.getVariable(RoomVarName.init_score.toString()).getStringValue());
				userAmount.amount(amount);				
				
				userAmounts.addSFSObject(userAmount.build().getSFSObject());
			}
			*/
			
			if (ext.getPlayerMode() == PlayerMode.REAL_MODE.ordinal()){
				SFSObject res = mahjong.gameFinishGame(player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString());
				if(res != null){					
					send(isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), res, users);
					return;
				}			
			}
			
			/*
			 * Response to client
			 */
			SFSArray sfsPlayerArray = new SFSArray();
			PlayerComparator c = new PlayerComparator();
			
			Collections.sort(players, c);
			
			for(int i = 0; i < players.size(); i++) {
				Player iPlayer = players.get(i);									
				User iUser = mahjong.getUserByPlayerId(iPlayer.getPlayerId(), users);
				Response.Builder sfsUser = new Response.Builder();
				sfsUser.name(iPlayer.getName())
						.point(iPlayer.getExtendedScore());
				if(ext.getPlayerMode() == PlayerMode.REAL_MODE.ordinal() && iUser != null){					
					SFSObject res = mahjong.useGetBalance(sfsUser, iUser, iPlayer, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString());
					if(res != null){
						send(isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), res, users);
						//return;
					}									
				}
				else{
					double amount = (iPlayer.getExtendedScore() * roomBet) / GameplayConfig.INIT_SCORE;
					sfsUser.money(amount);					
				}			
				
				if(iPlayer.isTenpai()){
					sfsUser.isWinner(true);								
				}
				else {
					sfsUser.isWinner(false);								
				}
				
				/*
				 * Add tile directly to the left tiles if this is the final tile on the wall
				 */
				iPlayer.getLeftTiles().add(tile);
				
				sfsUser.tiles(mahjong.tileArrayToTileIdArray(iPlayer.getLeftTiles()));				
				logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Player " + iPlayer.toString() + " " + iPlayer.getLeftTiles()));				
				sfsPlayerArray.addSFSObject(sfsUser.build().getSFSObject());			
			}
			
			Response.Builder object = new Response.Builder();
			object.winPos(player.getPlayerPosition())
					.losePos(player.getPlayerPosition())
					.loseTile(-1)
					.winTile(-1)
					.type(2);
						
			response.RON(object.build().getSFSObject())
					.players(sfsPlayerArray);					
			send(TileSet.RON.toString(), response.build().getSFSObject(), users);			
			ext.addAction(mahjong.getUserByPlayerId(player.getPlayerId(), users), isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString());
			ext.setMatchState(MatchState.SHOW_RESULT);
			
			return;
		}
				
		/*
		 * Check if player can call TSUMO
		 */
		boolean isComplete = false;
		
		if(!player.isDisconnect()){
			if(player.isSpecialComplete()){
				isComplete = true;
			}
			else if (player.isNormalComplete()){
				mahjong.calculateHanScore(player, tile);
				if(player.getYakuList().size() > 0){
					isComplete = true;
					player.setTsumo(true);
				}
			}
		}
				
		logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "tileSet: " + player.logTileSet()));

		player.setJustPickedTile(tile);
		
		List<Tile> newLeftTiles = player.getLeftTiles();
				
		for(int i = 0; i < users.size(); i++){
			User userTmp = users.get(i);
			Player playerTmp = mahjong.getPlayerById(userTmp.getId());
			
			/*
			 * Response for picked Player
			 */
			if(playerTmp.equals(player)){											
				
				if(!player.isRiichiWaiting()){
					
					Response.Builder object = new Response.Builder();
					object.winPos(playerTmp.getPlayerPosition())
							.losePos(playerTmp.getPlayerPosition())
							.loseTile(tile.getId())
							.winTile(Arrays.asList(tile.getId()));
					
					/*
					 * Check if the player can call TSUMO
					 */
					if(isComplete){
						response.TSUMO(object.build().getSFSObject());
						skipTypes.add(TileSet.TSUMO);					
					}
					
					/*
					 * Check if player can call RIICHI
					 */
					if(!player.isPublic() && player.isTenpai() && mahjong.getNumTileOnWall() > 3 && !player.isRiichiWaiting()){
						response.RIICHI(object.build().getSFSObject());
						skipTypes.add(TileSet.RIICHI);
					}
					
					/*
					 * Check if the player can call CLOSE KAN
					 */
					if(player.checkKan(tile)){						
						response.CLOSEKAN(object.build().getSFSObject());
						skipTypes.add(TileSet.CLOSEKAN);					
					}
					/*
					 * Check if the player can call LATE KAN
					 */
					if(player.checkLateKan(tile)){
						response.LATEKAN(object.build().getSFSObject());
						skipTypes.add(TileSet.LATEKAN);
					}
				}				
				
				send(isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), response.build().getSFSObject(), userTmp);
			}
			
			/*
			 * Response for other Player
			 */
			else {
				send(isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), response.build().getSFSObject(), userTmp);
			}
		}
		
		newLeftTiles.add(tile);
				
		player.setHasThrown(false);
		player.setHasPicked(true);
		player.setSkipTypes(skipTypes);
		player.setRon(false);
		
		mahjong.setPlayerInTurn(player);
		mahjong.setHasPickedTile(true);
		
		int numTileOnWall = mahjong.getNumTileOnWall();
		numTileOnWall--;
		mahjong.setNumTileOnWall(numTileOnWall);
		
		logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "numTileOnWall: " + numTileOnWall));
				
		/*
		 * Leave the TEMP FURITEM state
		 */
		if(player.isTempFuriten()){
			player.setTempFuriten(false);
		}
				
		if(user != null){
			ext.addAction(user, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString());
		}
		
		logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Picked Tile: " + tile.toString()));
		
		/*
		 * Start time in milliseconds
		 */
		long startTime = System.currentTimeMillis();
		player.setStartTime(startTime);
		
		logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Start time: " + startTime));
		logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Bonus time: " + player.getBonusTime()));	
		logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Send pickTile cmd"));
		
		if(player.isDisconnect() || Boolean.valueOf(room.getVariable(RoomVarName.is_auto.toString()).getStringValue()) || player.isRiichiWaiting()){
			
			Timer timer = player.getTimer();
			final SFSObject nextParams = new SFSObject();										
			int delay = player.getBonusTime() + player.getTurnTime();			
			logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "skipTypes: " + skipTypes));
			if(skipTypes.size() > 0 && !player.isDisconnect()){
				delay = player.getTurnTime();
				logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Calling skip handler: " + delay));				
				timer.schedule(new TimerTask() {
					  @Override
					  public void run() {						  
						  SkipHandler skipHandler = new SkipHandler();
						  skipHandler.skip(ext, user, nextParams, true);
					  }
				}, delay);
			}
			else {
				
				nextParams.putInt(MObject.tile.toString(), tile.getId());
								
				/*
				 * Only wait 1 second if the player is disconnected
				 */
				if(player.isDisconnect() || player.isRiichiWaiting()){
					delay = 1000;
				}
				
				logger.info(mahjong.log(room, player, isFromDeathWall ? GameActionType.pickTileFromDeathWall.toString() : GameActionType.pickTile.toString(), "Calling throw handler: " + delay));
				
				timer.schedule(new TimerTask() {
					  @Override
					  public void run() {
						  if(!player.isHasThrown()){
							  ThrowTileHandler throwTileHandler = new ThrowTileHandler();					  
							  throwTileHandler.throwTile(ext, user, nextParams, true, false);
						  }								
					  }
				}, delay);
			}
		}		
	}
}